﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace BitcoinLibrary
{
    /**
    * Abstract superclass of classes with list based payload, i.e. InventoryMessage and GetDataMessage.
    */
    public abstract class ListMessage : Message
    {
        private List<InventoryItem> items;
        private const long MAX_INVENTORY_ITEMS = 50000;

        public ListMessage(NetworkParameters param, byte[] bytes)
            : base(param, bytes, 0)
        {
        }

        public ListMessage(NetworkParameters param)
            : base(param)
        {
            items = new List<InventoryItem>();
        }

        public List<InventoryItem> getItems()
        {
            return items;
        }

        public void addItem(InventoryItem item)
        {
            items.Add(item);
        }

        public override void parse()
        {
            // An inv is vector<CInv> where CInv is int+hash. The int is either 1 or 2 for tx or block.
            long arrayLen = readVarInt();
            if (arrayLen > MAX_INVENTORY_ITEMS)
                throw new ProtocolException("Too many items in INV message: " + arrayLen);
            items = new List<InventoryItem>((int)arrayLen);
            for (int i = 0; i < arrayLen; i++)
            {
                if (cursor + 4 + 32 > bytes.Length)
                {
                    throw new ProtocolException("Ran off the end of the INV");
                }
                int typeCode = (int)readUint32();
                InventoryItem.Type type;
                // See ppszTypeName in net.h
                switch (typeCode)
                {
                    case 0: type = InventoryItem.Type.Error; break;
                    case 1: type = InventoryItem.Type.Transaction; break;
                    case 2: type = InventoryItem.Type.Block; break;
                    default:
                        throw new ProtocolException("Unknown CInv type: " + typeCode);
                }
                InventoryItem item = new InventoryItem(type, readHash());
                items.Add(item);
            }
            bytes = null;
        }

        public override void bitcoinSerializeToStream(Stream stream)
        {
            var data = new VarInt(items.Count).encode();
            stream.Write(data, 0, data.Length);
            foreach (InventoryItem i in items)
            {
                // Write out the type code.
                Utils.uint32ToByteStreamLE((int)i.type, stream);
                // And now the hash.
                stream.Write(Utils.reverseBytes(i.hash), 0, i.hash.Length);
            }
        }
    }
}
